﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using System.Text;
using System.Windows;
using RouteTiming.Data;
using RouteTiming.Helpers;
using RouteTiming.SLClient.DynamicModel.Helpers;
using RouteTiming.SLClient.Web;

namespace RouteTiming.SLClient.DynamicModel
{
    public class DynamicContext
    {
        // public properties

        public RouteTimingDomainContext Context { get; private set; }

        public ObservableCollection<DynamicRoute> Routes { get; private set; }

        public event EventHandler<OnErrorEventArgs> OnError;
        public event EventHandler<OnWarningEventArgs> OnWarning;
        public event EventHandler OnOperationCancelled;

        // public methods

        public DynamicContext() : this(new RouteTimingDomainContext())
        {
            
        }

        public DynamicContext(RouteTimingDomainContext context)
            : this(context, DateTime.Now, DateTime.Now + TimeSpan.FromDays(1))
        {
        }

        public DynamicContext(RouteTimingDomainContext context, DateTime timeFrom, DateTime timeTill)
        {
            this.Context = context;
            this._timeFrom = timeFrom;
            this._timeTill = timeTill;

            // initially loading routes
            this.Routes = new ObservableCollection<DynamicRoute>();
            this.Context.Load(this.Context.GetRouteSetQuery(), OnRoutesLoaded, null);
        }

        // private fields

        private readonly DateTime _timeFrom;
        private readonly DateTime _timeTill;

        private Int64 _lastEventOrderNo;

        // private methods

        private void OnRoutesLoaded(LoadOperation<Route> operation)
        {
            if (operation.HasError)
            {
                this.OnError.Raise(this, operation.Error.Message);
                operation.MarkErrorAsHandled();
                return;
            }

            if (operation.IsCanceled)
            {
                this.OnOperationCancelled.Raise(this);
                return;
            }

            if (operation.Entities != null)
            {
                foreach (var route in operation.Entities)
                {
                    this.Routes.Add(new DynamicRoute(route, this._timeFrom, this._timeTill));
                }

                this.Context.GetNewEvents(_lastEventOrderNo, OnNewEvents, null);
            }
        }

        private void OnNewEvents(InvokeOperation<IEnumerable<Event>> operation)
        {
            if (operation.HasError)
            {
                this.OnWarning.Raise(this, operation.Error.Message);
                operation.MarkErrorAsHandled();
            }

            // if at least any information arrived from server
            if (operation.Value != null)
            {
                foreach (var evt in operation.Value)
                {
                    this.OnEventAdded(evt);
                    this._lastEventOrderNo = evt.OrderNo;
                }
            }

            // continue polling in any way
            this.Context.GetNewEvents(_lastEventOrderNo, OnNewEvents, null);
        }

        private void OnEventAdded(Event evt)
        {
            foreach (var route in this.Routes)
            {
                route.ProcessEvent(evt);
            }
        }
    }
}
